

#include <bsp_usart.h>
#include <rtthread.h>
#include <board.h>

static uint8_t debug_uart_tx_buffer[DEBUG_UART_TX_BUFFER_SIZE];
static uint8_t debug_uart_rx_buffer[DEBUG_UART_RX_BUFFER_SIZE];
static volatile uint8_t debug_uart_rx_size = 0;
static volatile uint8_t debug_uart_rd_pos = 0;

static UART_HandleTypeDef debug_uart;
static UART_HandleTypeDef download_uart;

static uint8_t download_uart_tx_buffer[DEBUG_UART_TX_BUFFER_SIZE];
static uint8_t download_uart_rx_buffer[DEBUG_UART_RX_BUFFER_SIZE];
static volatile uint8_t download_uart_rx_size = 0;
static volatile uint8_t download_uart_rd_pos = 0;

static rt_sem_t uart_sem = RT_NULL;

#ifdef RT_USING_DEVICE
static rt_device_t debug_uart = RT_NULL;
#endif
/*
    源地址和目的地址的数据宽度可以不同，但是数据地址必须要跟其数据类型对齐。比如源地址是uint32 类型的，那么此数组的地址必须 4 字节对齐。
    DMA1 和 DMA2 是不支持外设到外设的传输，BDMA 是支持的，这个模式在低功耗模式下比较有用。
    使用存储器到存储器模式不支持循环传输模式，同时必须开启 FIFO，即不支持直接模式（关闭了 FIFO 就是直接模式 Direct mode）。
        Normal 正常模式
        适合用于单次传输，比如存储器到存储器的数据复制粘贴，又比如串口的数据单次发送，下次还需要
        发送的时候，使能下即可。
        Circular 循环模式
        适合用于需要连续传输的场合，比如定时器触发 BDMA 实现任意 IO 的 PWM 输出。

    突发传输的含义是每个 DMA 请求后可以连续传输的数据项目数，支持 4 次，8 次和 16 次。

    (1) 禁止 FIFO 的情况下，即 STM32H7 参考手册里面所说的直接模式 Direct Mode，务必要保证外设数
    据宽度和内存数据宽度是一样的，而且禁止了 FIFO 的情况下，不支持突发，即使配置了，也是无效的。
    (2) 禁止了 FIFO 的情况下，也不可用于存储器到存储器的数据传输，仅支持外设到存储器或者存储器到
    外设方式。
    (3) 使能 FIFO 的情况下，可以使用突发模式，也可以不使用。
    (4) 独立的源和目标传输宽度（字节、半字、字）：源和目标的数据宽度不相等时， DMA 自动封装/解
    封必要的传输数据来优化带宽。这个特性仅在 FIFO 模式下可用。这个特性非常重要，在 H7 使用
    SDIO 时要用到。无需像 F1 系列那样强行数据缓冲的 4 字节对齐要求。


*/

/// @brief debug uart initialization
/// @param
/// @return
int bsp_debug_uart_init(void)
{
    debug_uart.Instance = DEBUG_UART;
    debug_uart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    debug_uart.Init.BaudRate = 115200;
    debug_uart.Init.Mode = UART_MODE_TX_RX;
    debug_uart.Init.OverSampling = UART_OVERSAMPLING_16;
    debug_uart.Init.Parity = UART_PARITY_NONE;
    debug_uart.Init.StopBits = UART_STOPBITS_1;
    debug_uart.Init.WordLength = UART_WORDLENGTH_8B;
    debug_uart.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    HAL_UART_Init(&debug_uart);

    // SET_BIT(DEBUG_UART->ICR, USART_ICR_TCCF);   /* 清除 TC 发送完成标志 */
    // SET_BIT(DEBUG_UART->RQR, USART_RQR_RXFRQ);  /* 清除 RXNE 接收标志 */
    // SET_BIT(DEBUG_UART->CR1, USART_CR1_RXNEIE); /* 使能 PE. RX 接受中断 */

    return 0;
}

int bsp_download_uart_init(void)
{
    download_uart.Instance = DOWNLOAD_UART;
    download_uart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    download_uart.Init.BaudRate = 912600;
    download_uart.Init.Mode = UART_MODE_TX_RX;
    download_uart.Init.OverSampling = UART_OVERSAMPLING_16;
    download_uart.Init.Parity = UART_PARITY_NONE;
    download_uart.Init.StopBits = UART_STOPBITS_1;
    download_uart.Init.WordLength = UART_WORDLENGTH_8B;
    download_uart.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    HAL_UART_Init(&download_uart);

    // SET_BIT(DOWNLOAD_UART->ICR, USART_ICR_TCCF);   /* 清除 TC 发送完成标志 */
    // SET_BIT(DOWNLOAD_UART->RQR, USART_RQR_RXFRQ);  /* 清除 RXNE 接收标志 */
    // SET_BIT(DOWNLOAD_UART->CR1, USART_CR1_RXNEIE); /* 使能 PE. RX 接受中断 */

    return 0;
}

/// @brief re-implement console output string
/// @param str
void rt_hw_console_output(const char *str)
{
    uint16_t size = rt_strlen(str);

#ifndef DEBUG_UART_DMA
    uint16_t i = 0;
    char a = '\r';
    for (i = 0; i < size; i++)
    {
        if (*(str + i) == '\n')
        {
            HAL_UART_Transmit(&debug_uart, (uint8_t *)&a, 1, 1);
        }
        HAL_UART_Transmit(&debug_uart, (uint8_t *)(str + i), 1, 1);
    }

#else
    uint16_t total_send_bytes = size;
    uint16_t send_bytes = 0;
    uint8_t *tx_pos = (uint8_t *)str;
    while (total_send_bytes)
    {
        /* send_bytes is min value betwen size + 2, and DEBUG_UART_TX_BUFFER_SIZE */
        send_bytes = size;
        if (send_bytes > DEBUG_UART_TX_BUFFER_SIZE - 2)
            send_bytes = DEBUG_UART_TX_BUFFER_SIZE - 2;

        // rt_memcpy(debug_uart_tx_buffer, tx_pos, send_bytes);
        for (size_t i = 0; i < send_bytes; i++)
        {
            debug_uart_tx_buffer[i] = tx_pos[i];
        }

        debug_uart_tx_buffer[send_bytes] = '\r';
        debug_uart_tx_buffer[send_bytes + 1] = '\n';

        tx_pos += send_bytes;
        total_send_bytes -= send_bytes;
        size -= send_bytes;

        HAL_UART_Transmit_DMA(&debug_uart, debug_uart_tx_buffer, send_bytes + 2);
    }

#endif
}

/// @brief re-implement console getchar
/// @param
/// @return
char rt_hw_console_getchar(void)
{
    int ch = -1;
    if (!uart_sem)
    {
        uart_sem = rt_sem_create("uart_sem", 0, RT_IPC_FLAG_PRIO);
    }
    if (uart_sem)
        rt_sem_take(uart_sem, RT_WAITING_FOREVER);

    if (debug_uart_rx_size == 0)
        return ch;

    if (debug_uart_rx_size == debug_uart_rd_pos)
    {
        debug_uart_rx_size = debug_uart_rd_pos = 0;
    }

    ch = debug_uart_rx_buffer[debug_uart_rd_pos];
    debug_uart_rd_pos++;

    return ch;
}

void HAL_USART_RxCpltCallback(USART_HandleTypeDef *husart)
{
}

void HAL_USART_TxCpltCallback(USART_HandleTypeDef *husart)
{
    if (husart->Instance == DEBUG_UART)
    {
        HAL_UART_DMAStop(&debug_uart);
    }
}

/// @brief This function handles uart global interrupt.
/// @param
void DEBUG_UART_HANDLER(void)
{
    rt_interrupt_enter();

    if (__HAL_UART_GET_IT(&debug_uart, UART_IT_RXNE) == SET)
    {
        debug_uart_rx_buffer[debug_uart_rx_size] = debug_uart.Instance->RDR & 0xff;
        debug_uart_rx_size++;

        if (uart_sem)
            rt_sem_release(uart_sem);
    }

    HAL_UART_IRQHandler(&debug_uart);

    rt_interrupt_leave();
}

int bsp_debug_print_rx_buffer()
{
    rt_kprintf("debug uart receive %d bytes: ", debug_uart_rx_size);
    for (size_t i = 0; i < debug_uart_rx_size; i++)
    {
        rt_kprintf("%02x ", debug_uart_rx_buffer[i]);
    }
    rt_kprintf("\n");
    debug_uart_rx_size = 0;
    return 0;
}

int bsp_debug_print_dma(uint8_t *tx_buffer, uint16_t tx_size)
{
    HAL_UART_Transmit_DMA(&debug_uart, tx_buffer, tx_size);
    return 0;
}

int bsp_download_print_rx_buffer()
{
    rt_kprintf("usart1 receive %d bytes: ", download_uart_rx_size);
    for (size_t i = 0; i < download_uart_rx_size; i++)
    {
        rt_kprintf("%02x ", download_uart_rx_buffer[i]);
    }
    rt_kprintf("\n");
    download_uart_rx_size = 0;
    return 0;
}

int bsp_download_send_tx_buffer(uint8_t *tx_buf, uint16_t tx_cnt)
{
    HAL_UART_Transmit_DMA(&download_uart, tx_buf, tx_cnt);
    return 0;
}

void DOWNLOAD_UART_HANDLER(void)
{
    rt_interrupt_enter();

    HAL_UART_IRQHandler(&download_uart);

    rt_interrupt_leave();
}